﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using System.Diagnostics;
using DARE;
using System.IO;
using System.Reflection;
using Microsoft.Xna.Framework.Content.Pipeline.Serialization.Intermediate;
using System.Xml;
using System.ComponentModel;
using Microsoft.Xna.Framework;

namespace ContentProcessor
{
    [ContentProcessor(DisplayName = "Full Model Information - DARE")]
    public class CModelFullExporter : ModelProcessor
    {
        #region Fields

        private string m_dependenciesData;

        #endregion

        #region Property

        [TypeConverter(typeof(CDependencieConverter))]
        [Browsable(false)]
        public Dictionary<string, string> Dependencies { get; set; }

        //[Browsable(false)]
        public string DependenciesData
        {
            get { return m_dependenciesData; }
            set
            {
                if (!string.IsNullOrEmpty(value))
                {
                    Dependencies = new Dictionary<string, string>();
                    string[] link = value.Split("#".ToArray(), StringSplitOptions.RemoveEmptyEntries);
                    foreach (var elem in link)
                    {
                        string[] el = elem.Split('&');
                        if (el != null && el.Length == 2)
                            Dependencies.Add(el[0], el[1]);
                    }
                    m_dependenciesData = value;
                }
            }
        }

        #endregion

        #region CTOR

        public CModelFullExporter()
        {
        }

        #endregion

        #region Method

        public override ModelContent Process(NodeContent input, ContentProcessorContext context)
        {
            if (Dependencies != null)
                DependencieChanged(input);

            this.ResizeTexturesToPowerOfTwo = true;
            var model = base.Process(input, context);

            string _filePath = input.Identity.SourceFilename;
            _filePath = Path.Combine(Path.GetDirectoryName(_filePath), Path.GetFileNameWithoutExtension(_filePath));

            CModelTag m_modelTag = new CModelTag();
            m_modelTag.Add(CModel.MODELMINMAX, FindMinMax(model));

            model.Tag = m_modelTag;

            foreach (var mesh in model.Meshes)
            {
                foreach (var part in mesh.MeshParts)
                {
                    CEffectTextures _ets = new CEffectTextures();
                    Type _t = typeof(CEffectTextures);

                    foreach (var item in part.Material.OpaqueData)
                    {
                        PropertyInfo _pi = _t.GetProperty(item.Key, BindingFlags.Instance | BindingFlags.Public);
                        if (_pi != null)
                        {
                            _pi.SetValue(_ets, item.Value, null);
                        }
                        else
                        {
                            _ets.SetParam(item.Key, item.Value);
                            Console.WriteLine(item.Key + " is missing in the CEffectTextures");
                            context.Logger.LogWarning("", input.Identity, item.Key + " is missing in the CEffectTextures");
                        }
                    }
                    foreach (var _texture in part.Material.Textures)
                    {
                        string tmp = _texture.Value.Filename;
                        string original = _texture.Value.Filename;
                        if (Path.GetExtension(tmp) != ".xnb" && File.Exists(original))
                        {
                            var texure = context.BuildAsset<TextureContent, Texture2DContent>(_texture.Value, null);
                            original = texure.Filename;
                            tmp = original;
                        }
                        else if (Path.GetExtension(tmp) != ".xnb")
                            continue;

                        while (tmp != null && !tmp.EndsWith("content", StringComparison.CurrentCultureIgnoreCase))
                            tmp = Path.GetDirectoryName(tmp);

                        if (tmp == null)
                            continue;

                        var _modelDiff = context.OutputFilename.Replace(context.OutputDirectory, "");

                        string pathFile = original.Replace(tmp, "");

                        string tmpToRemove = "\\";
                        string[] _partsFile = pathFile.Split("\\".ToArray(), StringSplitOptions.RemoveEmptyEntries);
                        string[] _partsModel = _modelDiff.Split("\\".ToArray(), StringSplitOptions.RemoveEmptyEntries);

                        int _quantity = 0;
                        string to_remove = "";
                        for (int i = 0; i < _partsFile.Length; ++i)
                        {
                            if (_partsModel.Length > i && _partsFile[i] == _partsModel[i])
                                continue;
                            if (_quantity != 0)
                                to_remove += '\\';
                            to_remove += _partsFile[i];
                            _quantity++;
                        }

                        pathFile = to_remove;

                        if (pathFile.StartsWith(tmpToRemove))
                            pathFile = pathFile.Remove(0, 1);

                        _ets.AddTexture(_texture.Key, Path.Combine(Path.GetDirectoryName(pathFile), Path.GetFileNameWithoutExtension(pathFile)));
                    }
                    part.Tag = _ets;
                }
            }
            return model;
        }

        private void DependencieChanged(NodeContent node)
        {
            if (node is MeshContent)
            {
                int numberOfPart = 0;
                MeshContent _content = node as MeshContent;
                foreach (var elem in _content.Geometry)
                {
                    if (elem.Material != null)
                    {
                        TextureReferenceDictionary _dico = new TextureReferenceDictionary();
                        foreach (var _texture in elem.Material.Textures)
                        {
                            string key = string.Format("{0}-{1}-{2}", node.Name, elem.Name + numberOfPart.ToString(), _texture.Key);
                            if (Dependencies.ContainsKey(key))
                                _texture.Value.Filename = Dependencies[key];
                        }
                    }
                    numberOfPart++;
                }
            }

            foreach (NodeContent _node in node.Children)
            {
                DependencieChanged(_node);
            }
        }

        public BoundingBox FindMinMax(ModelContent _model)
        {
            Vector3 _min = Vector3.One * Int32.MaxValue;
            Vector3 _max = Vector3.One * Int32.MinValue;

            MinMaxVertex(_model, ref _min, ref _max);
            //AnimationMinMax(_model, ref _min, ref _max);
            return new BoundingBox(_min, _max);
        }

        private void MinMaxVertex(ModelContent _model, ref Vector3 min, ref Vector3 max)
        {
            Vector3 _min = min;
            Vector3 _max = max;
            foreach (var _mesh in _model.Meshes)
            {
                foreach (var _geo in _mesh.SourceMesh.Geometry)
                {
                    foreach (var _vertice in _geo.Vertices.Positions)
                    {
                        var vertice = Vector3.Transform(_vertice, _mesh.SourceMesh.AbsoluteTransform);
                        if (vertice.X < _min.X)
                            _min.X = vertice.X;
                        if (vertice.Y < _min.Y)
                            _min.Y = vertice.Y;
                        if (vertice.Z < _min.Z)
                            _min.Z = vertice.Z;

                        if (vertice.X > _max.X)
                            _max.X = vertice.X;
                        if (vertice.Y > _max.Y)
                            _max.Y = vertice.Y;
                        if (vertice.Z > _max.Z)
                            _max.Z = vertice.Z;
                    }
                }
            }
            min = _min;
            max = _max;
        }
        #endregion
    }
}
